package com.wallen.timedsmssender;

import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.loopj.android.http.MySSLSocketFactory;

import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import bolts.Task;

public class MainActivity extends Activity {
	private static final int UI_UPDATE_TIME = 200;
	private static final int SMS_SEND_TIME_RATE = 700;
	private static final int HTTP_SEND_TIME_RATE = 200;
	private String phoneNumber;
	private String baseUrl;
	private String path;
	AtomicInteger numberOfSentSMS = new AtomicInteger(0);
	AtomicInteger numberOfSentHTTP = new AtomicInteger(0);
	AtomicInteger numberOfScheduledSMS = new AtomicInteger(-1);
	AtomicInteger numberOfScheduledHTTP = new AtomicInteger(-1);
	private int updateTime = SMS_SEND_TIME_RATE;
	final private Handler mHandler = new Handler();
	private ArrayList<Pair<String, String>> pList = new ArrayList<>();
	private PairAdapter adapter;
	final Runnable HTTPTaskRunnable = new Runnable() {
		@Override
		public void run() {
			if (!baseUrl.isEmpty()) {
				Task.callInBackground(new Callable<Void>() {
					@Override
					public Void call() throws Exception {
						numberOfSentHTTP.getAndIncrement();
						CertificateFactory cf = CertificateFactory.getInstance("X.509");
						//InputStream caInput = new BufferedInputStream(getApplicationContext().getAssets().open
						// ("adsend"));
						//Certificate ca = cf.generateCertificate(caInput);
						//System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
						// Create a KeyStore containing our trusted CAs
						String keyStoreType = KeyStore.getDefaultType();
						KeyStore keyStore = KeyStore.getInstance(keyStoreType);
						keyStore.load(null, null);
						//keyStore.setCertificateEntry("ca", ca);
						// Create a TrustManager that trusts the CAs in our KeyStore
						String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
						TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
						tmf.init(keyStore);
						// Create an SSLContext that uses our TrustManager
						SSLContext context = SSLContext.getInstance("TLS");
						context.init(null, tmf.getTrustManagers(), null);
						//disable SSL checks
						disableSSLCertificateChecking();
						if (!baseUrl.contains("http://") && !baseUrl.contains("https://")) {
							baseUrl = "http://" + baseUrl;
						}
						Uri.Builder b = Uri.parse(baseUrl).buildUpon();
						b.path(path);
						parseParameters(b);
						System.out.println("URL was: " + b.toString());
						HttpGet method = new HttpGet(b.build().toString());
						HttpClient client = getNewHttpClient();
						HttpResponse response = client.execute(method);
						int responseCode = response.getStatusLine().getStatusCode();
						String text = responseCode + " - " + response.getStatusLine().getReasonPhrase() + " - " +
								new BasicResponseHandler().handleResponse(response);
						System.out.println(text);
						return null;
					}
				});
				if (numberOfScheduledHTTP.get() != 1) {
					if (numberOfScheduledHTTP.get() != -1) numberOfScheduledHTTP.getAndDecrement();
					mHandler.postDelayed(this, HTTP_SEND_TIME_RATE);
					mHandler.postDelayed(updateHTTPCounter, UI_UPDATE_TIME);
				} else {
					httpResetCondition();
					mHandler.postDelayed(updateHTTPCounter, UI_UPDATE_TIME);
				}
				mHandler.postDelayed(updateHTTPCounter, UI_UPDATE_TIME);
			} else {
				Toast.makeText(getApplicationContext(), "Invalid address.", Toast.LENGTH_LONG).show();
			}
		}
	};

	private void parseParameters(Uri.Builder b) {
		for (Pair p : pList) {
			if (!p.equals(new Pair("", ""))) {
				b.appendQueryParameter(String.valueOf(p.first), String.valueOf(p.second));
			}
		}
	}

	final Runnable SMSTaskRunnable = new Runnable() {
		@Override
		public void run() {
			numberOfSentSMS.incrementAndGet();
			SmsManager smsManager = SmsManager.getDefault();
			String text = "Test" + numberOfSentSMS;
			smsManager.sendTextMessage(phoneNumber, null, text, null, null);
			if (numberOfScheduledSMS.get() != 1) {
				if (numberOfScheduledSMS.get() != -1) {
					numberOfScheduledSMS.getAndDecrement();
				}
				mHandler.postDelayed(this, SMS_SEND_TIME_RATE);
				mHandler.postDelayed(updateSMSCounter, UI_UPDATE_TIME);
			} else {
				smsResetCondition();
				mHandler.postDelayed(updateSMSCounter, UI_UPDATE_TIME);
			}
		}
	};
	final Runnable UDPTaskRunnable = new Runnable() {
		@Override
		public void run() {
			Thread t = new Thread() {
				@Override
				public void run() {
					try {
						for (int i = 0; i < 50; i++) {
							String payload = "";
							String address;
							ArrayList<String> addressParsed = new ArrayList<>();
							for (Pair<String, String> p : pList) {
								if (!String.valueOf(p.first).isEmpty() && !String.valueOf(p.second).isEmpty())
									payload += String.valueOf(p.first) + "=" + String.valueOf(p.second) + ";";
							}
							System.out.println("Payload ready: " + payload);
							if (!baseUrl.isEmpty()) address = baseUrl;
							else address = "127.0.0.1:80";
							address = address.replaceAll("(?i)http://", "");
							address = address.replace("(?i)https://", "");
							StringTokenizer st = new StringTokenizer(address, ":");
							while (st.hasMoreTokens()) {
								addressParsed.add(st.nextToken());
							}
							if (addressParsed.size() == 1 && addressParsed.get(0).contains("."))
								addressParsed.add("80");
							if (addressParsed.size() == 2) {
								System.out.println("Address is: " + addressParsed.get(0) + ":" + Integer.valueOf
										(addressParsed.get(1)));
								DatagramPacket dp = new DatagramPacket(payload.getBytes(), payload.getBytes()
										.length, new InetSocketAddress(addressParsed.get(0), Integer.valueOf
										(addressParsed.get(1))));
								DatagramSocket ds = new DatagramSocket();
								ds.send(dp);
							} else {
								Toast.makeText(getApplicationContext(), "Invalid address.", Toast.LENGTH_LONG)
										.show();
								System.out.println("Invalid address.");
							}
						}
					} catch (SocketException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			};
			t.start();
		}
	};
	final Runnable updateSMSCounter = new Runnable() {
		@Override
		public void run() {
			EditText mEditText = (EditText) findViewById(R.id.smsSentNumberInput);
			mEditText.setText(String.valueOf(numberOfSentSMS.get()));
			//mHandler.postDelayed(this, UI_UPDATE_TIME);
		}
	};
	final Runnable updateHTTPCounter = new Runnable() {
		@Override
		public void run() {
			EditText mEditText = (EditText) findViewById(R.id.httpSentNumberInput);
			mEditText.setText(String.valueOf(numberOfSentHTTP.get()));
			//mHandler.postDelayed(this, UI_UPDATE_TIME);
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		  /*
		  Set content view
         */
		setContentView(R.layout.activity_main);
		  /*
		  Get UI elements
         */
		final Button startButton = (Button) findViewById(R.id.startSMS);
		final Button stopButton = (Button) findViewById(R.id.stopSMS);
		final Button addPairButton = (Button) findViewById(R.id.addToBundle);
		final Button startPing = (Button) findViewById(R.id.startPing);
		final Button stopPing = (Button) findViewById(R.id.stopPing);
		final Button startUDP = (Button) findViewById(R.id.startUDP);
		  /*
		  Init vars
         */
		//number of sms to be sent
		((EditText) findViewById(R.id.smsScheduledNumberInput)).setText(String.valueOf(numberOfScheduledSMS
				.get()));
		//sms send time rate
		((EditText) findViewById(R.id.smsSendIntervalTimeInput)).setText(String.valueOf(SMS_SEND_TIME_RATE));
		//initial number of http requests
		((EditText) findViewById(R.id.httpScheduledNumberInput)).setText(String.valueOf(numberOfScheduledHTTP
				.get()));
		  /*
		  Disable stop buttons
         */
		stopButton.setEnabled(false);
		stopPing.setEnabled(false);
		addPairButton.setVisibility(View.INVISIBLE);
		  /*
        SMS START BUTTON
         */
		startButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				removeSMSDrawables();
				boolean isInputValid = true;
				numberOfSentSMS.set(0);
				numberOfScheduledSMS.set(0);
				String timeString = String.valueOf(((EditText) findViewById(R.id.smsSendIntervalTimeInput))
						.getText());
				String scheduledSMSString = String.valueOf(((EditText) findViewById(R.id
						.smsScheduledNumberInput)).getText());
				phoneNumber = ((EditText) findViewById(R.id.phoneNumber)).getText().toString();
				if (!scheduledSMSString.matches("-?\\d+")) {
					isInputValid = false;
					Drawable d = getResources().getDrawable(R.drawable.ic_error_black_18dp);
					((EditText) findViewById(R.id.smsScheduledNumberInput))
							.setCompoundDrawablesWithIntrinsicBounds(null, null, d, null);
				} else {
					if (Integer.parseInt(scheduledSMSString) == 0 || Integer.parseInt(scheduledSMSString) < -1) {
						isInputValid = false;
						Drawable d = getResources().getDrawable(R.drawable.ic_error_black_18dp);
						((EditText) findViewById(R.id.smsScheduledNumberInput))
								.setCompoundDrawablesWithIntrinsicBounds(null, null, d, null);
					}
				}
				if (!phoneNumber.matches("\\+?\\d+")) {
					isInputValid = false;
					Drawable d = getResources().getDrawable(R.drawable.ic_error_black_18dp);
					((EditText) findViewById(R.id.phoneNumber)).setCompoundDrawablesWithIntrinsicBounds(null,
							null, d, null);
				}
				if (!timeString.matches("\\d+")) {
					isInputValid = false;
					Drawable d = getResources().getDrawable(R.drawable.ic_error_black_18dp);
					((EditText) findViewById(R.id.smsSendIntervalTimeInput))
							.setCompoundDrawablesWithIntrinsicBounds(null, null, d, null);
				}
				if (isInputValid) {
					removeSMSDrawables();
					updateTime = Integer.parseInt(timeString);
					numberOfScheduledSMS.set(Integer.parseInt(scheduledSMSString));
					Toast.makeText(getApplicationContext(), "SMS test started.", Toast.LENGTH_LONG).show();
					mHandler.postDelayed(SMSTaskRunnable, updateTime);
					mHandler.postDelayed(updateSMSCounter, UI_UPDATE_TIME);
					stopButton.setEnabled(true);
					startButton.setEnabled(false);
				} else {
					Toast.makeText(getApplicationContext(), "Stopping SMS test.", Toast.LENGTH_LONG).show();
				}
			}
		});
        /*
        SMS STOP BUTTON
         */
		stopButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				smsResetCondition();
				Toast.makeText(getApplicationContext(), "Stopping SMS test.", Toast.LENGTH_LONG).show();
			}
		});
        /*
        HTTP START BUTTON
         */
		startPing.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				removeHTTPDrawables();
				boolean isInputValid = true;
				numberOfScheduledHTTP.set(0);
				numberOfSentHTTP.set(0);
				String scheduledHTTPString = String.valueOf(((EditText) findViewById(R.id
						.httpScheduledNumberInput)).getText());
				if (!scheduledHTTPString.matches("-?\\d+")) {
					isInputValid = false;
					Drawable d = getResources().getDrawable(R.drawable.ic_error_black_18dp);
					((EditText) findViewById(R.id.httpScheduledNumberInput))
							.setCompoundDrawablesWithIntrinsicBounds(null, null, d, null);
				} else {
					if (Integer.parseInt(scheduledHTTPString) == 0 || Integer.parseInt(scheduledHTTPString) <
							-1) {
						isInputValid = false;
						Drawable d = getResources().getDrawable(R.drawable.ic_error_black_18dp);
						((EditText) findViewById(R.id.httpScheduledNumberInput))
								.setCompoundDrawablesWithIntrinsicBounds(null, null, d, null);
					}
				}
				if (isInputValid) {
					removeHTTPDrawables();
					baseUrl = String.valueOf(((EditText) findViewById(R.id.baseURL)).getText());
					path = String.valueOf(((EditText) findViewById(R.id.pathURL)).getText());
					numberOfScheduledHTTP.set(Integer.parseInt(scheduledHTTPString));
					startPing.setEnabled(false);
					stopPing.setEnabled(true);
					Toast.makeText(getApplicationContext(), "TCP test started.", Toast.LENGTH_LONG).show();
					mHandler.postDelayed(HTTPTaskRunnable, HTTP_SEND_TIME_RATE);
					//mHandler.postDelayed(updateHTTPCounter, UI_UPDATE_TIME);
				} else {
					Toast.makeText(getApplicationContext(), "Invalid input.", Toast.LENGTH_LONG).show();
				}
			}
		});
		startUDP.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				removeHTTPDrawables();
				baseUrl = String.valueOf(((EditText) findViewById(R.id.baseURL)).getText());
				path = String.valueOf(((EditText) findViewById(R.id.pathURL)).getText());
				Toast.makeText(getApplicationContext(), "UDP test started.", Toast.LENGTH_LONG).show();
				mHandler.postDelayed(UDPTaskRunnable, 0);
			}
		});
        /*
        HTTP STOP BUTTON
         */
		stopPing.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				httpResetCondition();
				Toast.makeText(getApplicationContext(), "Stopping TCP test.", Toast.LENGTH_LONG).show();
			}
		});
        /*
        Set addpair button behavior
         */
		addPairButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Pair p = new Pair("", "");
				if (!pList.get(pList.size() - 1).equals(p)) pList.add(p);
				adapter.notifyDataSetChanged();
			}
		});
		//Get DeviceID
		TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		String deviceId = telephonyManager.getDeviceId();
		if (deviceId.isEmpty()) {
			deviceId = "N/A";
		}
		Pair<String, String> p1 = new Pair<>("DeviceID", deviceId);
		//Pair<String,String> p2 = new Pair<>("hard","coded");
		//Pair<String,String> p3 = new Pair<>("sup","sup");
//		Pair<String, String> p4 = new Pair<>("", "");
		pList.add(p1);
//		pList.add(p4);
		adapter = new PairAdapter(this, pList);
		NonScrollListView nsl = (NonScrollListView) findViewById(R.id.nsl);
		nsl.setAdapter(adapter);
	}

	private void httpResetCondition() {
		mHandler.removeCallbacks(HTTPTaskRunnable);
		final Button startPing = (Button) findViewById(R.id.startPing);
		final Button stopPing = (Button) findViewById(R.id.stopPing);
		startPing.setEnabled(true);
		stopPing.setEnabled(false);
		//mHandler.removeCallbacks(updateHTTPCounter);
	}

	private void smsResetCondition() {
		final Button startButton = (Button) findViewById(R.id.startSMS);
		final Button stopButton = (Button) findViewById(R.id.stopSMS);
		mHandler.removeCallbacks(SMSTaskRunnable);
		startButton.setEnabled(true);
		stopButton.setEnabled(false);
		//mHandler.removeCallbacks(updateSMSCounter);
	}

	private void removeHTTPDrawables() {
		((EditText) findViewById(R.id.httpScheduledNumberInput)).setCompoundDrawablesWithIntrinsicBounds(null,
				null, null, null);
	}

	private void removeSMSDrawables() {
		((EditText) findViewById(R.id.phoneNumber)).setCompoundDrawablesWithIntrinsicBounds(null, null, null,
				null);
		((EditText) findViewById(R.id.smsSendIntervalTimeInput)).setCompoundDrawablesWithIntrinsicBounds(null,
				null, null, null);
		((EditText) findViewById(R.id.smsScheduledNumberInput)).setCompoundDrawablesWithIntrinsicBounds(null,
				null, null, null);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.menu_main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();
		return id == R.id.action_settings || super.onOptionsItemSelected(item);
	}

	private static void disableSSLCertificateChecking() {
		TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
			@Override
			public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s)
					throws java.security.cert.CertificateException {
				// not implemented
			}

			@Override
			public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s)
					throws java.security.cert.CertificateException {
				// not implemented
			}

			@Override
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		}};
		try {
			HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
				@Override
				public boolean verify(String s, SSLSession sslSession) {
					return true;
				}
			});
			SSLContext sc = SSLContext.getInstance("TLS");
			sc.init(null, trustAllCerts, new java.security.SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
		} catch (KeyManagementException | NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}

	public HttpClient getNewHttpClient() {
		try {
			KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			trustStore.load(null, null);
			MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
			sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
			HttpParams params = new BasicHttpParams();
			HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
			HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
			SchemeRegistry registry = new SchemeRegistry();
			registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
			registry.register(new Scheme("https", sf, 443));
			ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
			return new DefaultHttpClient(ccm, params);
		} catch (Exception e) {
			return new DefaultHttpClient();
		}
	}

	public static String randomStringOfLength(int length) {
		StringBuffer buffer = new StringBuffer();
		while (buffer.length() < length) {
			buffer.append(uuidString());
		}
		//this part controls the length of the returned string
		return buffer.substring(0, length);
	}

	private static String uuidString() {
		return UUID.randomUUID().toString().replaceAll("-", "");
	}
}